#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>


#include "icar.h"
#include "icar_log.h"
#include "icar_msg.h"
#include "icar_utils.h"
#include "icar_sock.h"

uint16_t icar_msg_seq = 0;
static time_t global_ts = 0;
static int first_send = 0;

static int handle_conn_msg(int, uint8_t *msg, size_t size);
static int handle_std_msg(int, uint8_t *msg, size_t size);

int
icar_create_server(int type, int port, const char *address)
{
	if (type != SOCK_STREAM && type != SOCK_DGRAM)
		return -1;

	struct sockaddr_in addr;
	int fd;

	fd = socket(AF_INET, type, 0);
	if (fd < 0)
		return -1;

	bzero(&addr, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons((uint16_t)port);
	if (!address)
		addr.sin_addr.s_addr = INADDR_ANY;
	else
		addr.sin_addr.s_addr = inet_addr(address);

#ifdef SO_REUSEADDR
	int opt = 1;
	setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
#endif
	if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) 
	{
		close(fd);
		return -1;
	}

	if (listen(fd, 1024) < 0)
	{
		close(fd);
		return -1;
	}

	return fd;
}

int icar_get_connection(int fd, struct timeval *timeout)
{
	int result;
	fd_set fds;
	struct sockaddr_in addr;
	socklen_t addrlen;

	FD_ZERO(&fds);
	FD_SET(fd, &fds);
	result = 0;

	while (result == 0)
	{
		result = select(fd+1, &fds, 0, 0, timeout);
		if (result < 0)
			return -1;
		if (timeout != 0 && result == 0)
			return -1;
		if (result > 0)
			break;
	}

	bzero(&addr, sizeof(addr));
	addrlen = sizeof(addr);
	return accept(fd, (struct sockaddr *)&addr, &addrlen);
}

int
_icar_net_read(sock, buf, len)
	int sock;
	uint8_t *buf;
	int len;
{
#if defined(_WIN32)
	return (recv(sock, buf, len, 0));
#else
	return read(sock, buf, len);
#endif
}

int icar_readPkg(uint8_t *destBuf, int len,  uint8_t *src, size_t size)
{
	uint8_t curChar,
		 *dst,
		 *pos;
	int count, remain = size;

	count = 0;
	dst = destBuf;
	pos = src;

	while (count < len)
	{
		if (remain == 0)
			return 0;

		curChar = *pos;
		pos++;
		remain--;

		if (count == 0) {
			if (curChar == 0x7e) {
				count++;
				*dst = curChar;
				continue;
			}
			else {
				*dst = 0;
				return 0;
			}
		}
		if (count == 1) {
			if (curChar == 0x7e) {
				*dst = curChar;
				continue;
			} else {
				dst++;
				*dst++ = curChar;
				count++;
				continue;
			}
		}

		*dst++ = curChar;
		count++;
		if (curChar == 0x7e)
			break;
	}

	return count;
}


int icar_process_request(int fd, uint8_t *src, size_t len)
{
	uint8_t data[READ_BUF_LEN] = { 0 };
	int count = 0;
	icar_daemon(LOG_DEBUG, "Process icar request");

	count = icar_pkg_del_head_tail(data, src, len);
	if (count < 0 || fd < 0)
		return -1;
	icar_daemon(LOG_DEBUG, "Delete package head and tail: %d\n", count);
	icar_parse_recv(fd, data, (size_t) count);
	return 0;
}

static const struct {
	const char *desc;
	int flag;
} icarmsg_flag[] = {
	{ "V1",     MSG_FLAG_V1},
	{ "V4",     MSG_FLAG_V4},
	{ "V8",     MSG_FLAG_V8},
	{ "S6",     MSG_FLAG_S6},
	{ "S17",    MSG_FLAG_S17},
	{ "S20",    MSG_FLAG_S20},
	{ "S32",    MSG_FLAG_S32 },
	{ "S81",    MSG_FLAG_S81},
	{ "R7",     MSG_FLAG_R7 },
	{ "R8",     MSG_FLAG_R8 },
	{ "I3",     MSG_FLAG_I3 },
	{ "I7",     MSG_FLAG_I7 },
	{ "I10",    MSG_FLAG_I10 },
	{ "I11",    MSG_FLAG_I11 },
	{ "I12",    MSG_FLAG_I12 },
	{ "I13",    MSG_FLAG_I13 },
	{ "I14",    MSG_FLAG_I14 },
	{ NULL, 	0}
};

static const char *
lookup_msg_type(int flag)
{
	int i = 0;
	for (i = 0; i < ARRAY_SIZE(icarmsg_flag); i++)
		if (icarmsg_flag[i].flag == flag)
			return icarmsg_flag[i].desc;

	return NULL;
}

static int
lookup_msg_val(const char *desc)
{
	int i;
	for (i = 0; i < ARRAY_SIZE(icarmsg_flag); i++) 
		if (strncasecmp(icarmsg_flag[i].desc, desc, strlen(icarmsg_flag[i].desc)) == 0)
			return icarmsg_flag[i].flag;

	return 0;
}

int 
icar_parse_recv(int fd, uint8_t *msg, size_t size)
{
	if (!msg) {
		icar_daemon(LOG_CRIT, "Introdacted error message body");
		return -1;
	}

	switch (msg[0]) {
	case '*':
		handle_conn_msg(fd, msg, size);
		break;
	case 0x24:
	case 0x23:
		handle_std_msg(fd, msg, size);
		break;
	default:
		icar_pkg_print(msg, size);
		return -1;
	}

	return 0;
}

static int
handle_conn_msg(int fd, uint8_t *msg, size_t size)
{
	char *p, *q;
	int i;
	char vehicle[20] = { 0 };
	int flag;
	char pro[VALUE_LEN] = { 0 };
	uint8_t dest[READ_BUF_LEN];
	int count = 0;

	icar_daemon(LOG_DEBUG, "[%s]", msg);

	bzero(dest, sizeof(dest));

	p = q = msg;
	i = 0;
	ICAR_MSG(p, q, i, size);
	if (i >= size)
		return -1;
	*q = 0;

	strncpy(pro, p, VALUE_LEN);

	ICAR_MSG(p, q, i, size);
	if (i >= size)
		return -1;
	*q = 0;
	strncpy(vehicle, p, sizeof(vehicle));

	ICAR_MSG(p, q, i, size);
	if (i >= size)
		return -1;
	*q = 0;

	flag = lookup_msg_val(p);
	icar_daemon(LOG_DEBUG, "Received message type: %s", p);
	switch (flag) {
	case MSG_FLAG_V1:
		{
			if ((count = flat_query_status(dest, pro, strlen(pro))) > 0) {
				write(fd, dest, count);
			}
		}
		break;
	case MSG_FLAG_V8:
		{
			if (first_send == 0) {
				first_send = 1;
				global_ts = time(NULL);
				if ((count = flat_diagnose(dest, pro, strlen(pro))) > 0)
					write(fd, dest, count);
			} else if (time(NULL) - global_ts >= 60) {
				if ((count = flat_diagnose(fd, pro, strlen(pro))) > 0)
					write(fd, dest, count);
				global_ts = time(NULL);
			}
		}
		break;
	case MSG_FLAG_V4:
		{
			ICAR_MSG(p, q, i, size);
			if (i == size)
				return -1;
			*q = 0;
			icar_daemon(LOG_DEBUG, "Replied message type: %s", p);
			ICAR_MSG(p, q, i, size);
			if (i == size)
				return -1;
			*q = 0;
			icar_daemon(LOG_DEBUG, "Flat time: %c%c:%c%c:%c%c", p[0], p[1],
					p[2], p[3], p[4], p[5]);
		}
		break;
	case MSG_FLAG_S6:
	case MSG_FLAG_S17:
	case MSG_FLAG_S20:
	case MSG_FLAG_S32:
	case MSG_FLAG_S81:
		p = ++q;
		icar_daemon(LOG_DEBUG, "%s", p);
		break;
	case MSG_FLAG_R7:
	case MSG_FLAG_R8: 
		p = ++q;
		icar_daemon(LOG_DEBUG, "%s", p);
		break;
	case MSG_FLAG_I3:
	case MSG_FLAG_I7:
	case MSG_FLAG_I10:
		break;
	case MSG_FLAG_I11:
		p = ++q;
		i++;
		icar_pkg_print(p, size - i);
		return 0;
	case MSG_FLAG_I12:
	case MSG_FLAG_I13:
		p = ++q;
		icar_daemon(LOG_DEBUG, "%s", p);
		break;
	case MSG_FLAG_I14:
		return 0;
	default:
		icar_daemon(LOG_ERR, "Unknown information type!");
		return -1;
	}

	ICAR_MSG(p, q, i, size);
	if (i >= size)
		return -1;
	*q = 0;

	icar_daemon(LOG_DEBUG, "Time: %c%c:%c%c:%c%c", p[0], p[1], p[2], p[3], p[4], p[5]);

	ICAR_MSG(p, q, i, size);
	if (i >= size)
		return -1;
	*q = 0;

	icar_daemon(LOG_DEBUG, "Valid bit: %s", p);

	ICAR_MSG(p, q, i, size);
	if (i >= size)
		return -1;
	*q = 0;

	icar_daemon(LOG_DEBUG, "Latitude: %s", p);

	ICAR_MSG(p, q, i, size);
	if (i >= size)
		return -1;
	*q = 0;

	if (*p == 'N')
		icar_daemon(LOG_DEBUG, "Nothern latitude!");
	else if (*p == 'S')
		icar_daemon(LOG_DEBUG, "Southern latitude!");
	else
		return -1;

	ICAR_MSG(p, q, i, size);
	if (i >= size)
		return -1;
	*q = 0;

	icar_daemon(LOG_DEBUG, "Longitude: %s", p);

	ICAR_MSG(p, q, i, size);
	if (i >= size)
		return -1;

	*q = 0;

	if (*p == 'E')
		icar_daemon(LOG_DEBUG, "East longitude!");
	else if (*p == 'W')
		icar_daemon(LOG_DEBUG, "West longitude!");
	else
		return -1;

	ICAR_MSG(p, q, i, size);
	if (i >= size)
		return -1;
	*q = 0;

	icar_daemon(LOG_DEBUG, "Speed: %s", p);

	ICAR_MSG(p, q, i, size);
	if (i >= size)
		return -1;
	*q = 0;
	icar_daemon(LOG_DEBUG, "Azimuth: %s", p);

	ICAR_MSG(p, q, i, size);
	if (i >= size)
		return -1;

	*q  = 0;
	if (strlen(p) == 6)
		icar_daemon(LOG_DEBUG, "Date: 20%c%c-%c%c-%c%c", p[4], p[5], p[2], p[3], p[0], p[1]);
	else
		return -1;

	ICAR_MSG(p, q, i, size);
	if (i >= size)
		return -1;
	*q = 0;

	icar_daemon(LOG_DEBUG, "Vehicle status: %s", p);

	ICAR_MSG(p, q, i, size);
	if (i >= size)
		return -1;
	*q = 0;

	icar_daemon(LOG_DEBUG, "Unkown flag: %s", p);

	ICAR_MSG(p, q, i, size);
	if (i >= size)
		return -1;
	*q = 0;
	icar_daemon(LOG_DEBUG, "Alarm flag: %s", p);

	ICAR_MSG(p, q, i, size);
	if (i >= size)
		return -1;
	*q = 0;
	icar_daemon(LOG_DEBUG, "Mileage: %s Km", p);

	ICAR_MSG(p, q, i, size);
	if (i >= size)
		return -1;
	*q = 0;

	icar_daemon(LOG_DEBUG, "Data type: %s", p);

	ICAR_MSG(p, q, i, size);
	if (i >= size)
		return -1;
	*q = 0;

	icar_daemon(LOG_DEBUG, "Country code: %s", p);

	ICAR_MSG(p, q, i, size);
	if (i >= size)
		return -1;
	*q = 0;
	icar_daemon(LOG_DEBUG, "Business number: %s", p);

	ICAR_MSG(p, q, i, size);
	if (i >= size)
		return -1;
	*q = 0;
	icar_daemon(LOG_DEBUG, "Battery: %s", p);
	ICAR_MSG(p, q, i, size);
	if (*q == '#') {
		*q = 0;
		icar_daemon(LOG_DEBUG, "Over%s%s", strlen(p) > 0 ? ": " : "", strlen(p) > 0 ? p : "");
	} else {
		*q = 0;
		icar_daemon(LOG_DEBUG, "%s", p);
	}

	return 0;
}

static int
handle_std_msg(int fd, uint8_t *msg, size_t size)
{
	return icar_parse_end(fd, msg[0], msg, size);
}
